package com.rankeiot.core.sqltoy;

import com.fasterxml.jackson.databind.JavaType;
import com.rankeiot.core.Context;
import com.rankeiot.core.data.PropMap;
import com.rankeiot.core.util.JSONUtil;
import com.rankeiot.core.util.StringUtil;
import com.rankeiot.core.util.castor.AnyCastor;
import com.rankeiot.core.util.castor.Castor;
import org.sagacity.sqltoy.SqlToyContext;
import org.sagacity.sqltoy.plugins.TypeHandler;
import org.sagacity.sqltoy.utils.BeanUtil;
import org.springframework.context.ApplicationContext;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

public class JsonTypeHandler extends TypeHandler {
    private static final Map<String,Class> primitivesMap=new HashMap<>();
    private static final Set<Class> primitiveTypes=new HashSet<>();
    static {
        primitivesMap.put("int",int.class);
        primitivesMap.put("long",long.class);
        primitivesMap.put("char",char.class);
        primitivesMap.put("boolean",boolean.class);
        primitivesMap.put("float",float.class);
        primitivesMap.put("double",double.class);
        primitivesMap.put("byte",byte.class);
        primitivesMap.put("short",short.class);
        primitiveTypes.add(Integer.class);
        primitiveTypes.add(Long.class);
        primitiveTypes.add(Character.class);
        primitiveTypes.add(Boolean.class);
        primitiveTypes.add(Float.class);
        primitiveTypes.add(Double.class);
        primitiveTypes.add(Byte.class);
        primitiveTypes.add(Date.class);
        primitiveTypes.add(java.sql.Date.class);
        primitiveTypes.add(LocalDate.class);
        primitiveTypes.add(LocalDateTime.class);
        primitiveTypes.add(BigInteger.class);
        primitiveTypes.add(BigDecimal.class);
        primitiveTypes.add(String.class);
        primitiveTypes.add(Timestamp.class);
        primitiveTypes.add(Time.class);
    }

    @Override
    public boolean setValue(Integer dbType, PreparedStatement pst, int paramIndex, int jdbcType, Object value) throws SQLException {
        if(value==null){
            return false;
        }
        if(value instanceof Enum){
            pst.setString(paramIndex,((Enum) value).name());
            return true;
        }
        if(value instanceof Collection){
            Collection collection=(Collection)value;
            if(collection.size()==0){
                pst.setString(paramIndex,"");
                return true;
            }
            Object obj = collection.stream().iterator().next();
            //obj.getClass().isPrimitive()
            Class type=obj.getClass();
            if(type.isPrimitive()||primitiveTypes.contains(type)){
                pst.setString(paramIndex, StringUtil.join(collection));
               return true;
            }
            String str= JSONUtil.toJSONString(value);
            pst.setString(paramIndex,str);
            return true;
        }
        Class type=value.getClass();
        if(type.isPrimitive()||primitiveTypes.contains(type)){
            return false;
        }else{
            String str=JSONUtil.toJSONString(value);
            pst.setString(paramIndex,str);
            return true;
        }
    }

    @Override
    public Object toJavaType(String javaTypeName, Class genericType, Object jdbcValue) throws Exception {

        if (jdbcValue == null) {
            return null;
        }
        if (jdbcValue instanceof String) {

        } else {
            return null;
        }
        String strValue=(String)jdbcValue;
        Class t = primitivesMap.get(javaTypeName);
        if(t==null) {
            t = Class.forName(javaTypeName);
        }
        if (genericType == null) {
            if(Enum.class.isAssignableFrom(t)){
              return Enum.valueOf(t,strValue);
            }
            Castor c= Castor.getCastor(t);
            if(c!=null){
                return c.cast(jdbcValue);
            }
            return JSONUtil.parseObject(strValue,t);
        } else {
            if(PropMap.class == t){
                JavaType jt= JSONUtil.getType(t,genericType);
                return JSONUtil.parseObject(strValue,jt);
            }else if (Map.class.isAssignableFrom(t)) {
                JavaType jt= JSONUtil.getType(t,String.class,genericType);
                return JSONUtil.parseObject(strValue,jt);
            }else if(Collection.class.isAssignableFrom(t)){
                if(StringUtil.isEmpty(strValue)){
                    if(Set.class.isAssignableFrom(t)){
                        return new HashSet<>();
                    }else{
                        return new ArrayList<>();
                    }
                }
                if(strValue.startsWith("[")&&strValue.endsWith("]")){
                    JavaType jt= JSONUtil.getType(t,genericType);
                    return JSONUtil.parseObject(strValue,jt);
                }else{
                    String[] values=strValue.split(",");
                    Collection ret=new ArrayList();
                    if(Set.class.isAssignableFrom(t)){
                        ret=new HashSet();
                    }
                    Castor c= Castor.getCastor(genericType);
                    if(c==null){
                        c= AnyCastor.instance;
                    }
                    for(String v:values){
                        ret.add(c.cast(v));
                    }
                    return ret;
                }
            }
            JavaType jt= JSONUtil.getType(t,genericType);
            return JSONUtil.parseObject(strValue,jt);

        }
    }
}
